import contextlib
import os
import threading as my_threading
import time
import json
# import jsonschema
from functools import wraps
from json import JSONDecodeError
from typing import Iterable, Iterator
import yaml
from tkinter import *


def singleton(cls):
    @wraps(cls)
    def run(*args, **kwargs):
        if not hasattr(cls, "_instance"):
            cls._instance = cls(*args, **kwargs)
        return cls._instance

    return run


class MyError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return '\n' + self.value


@contextlib.contextmanager
def try_ex(obj_msg: tuple, *args_msg):
    """
    :param obj_msg: Err对象的属性, 改属性包含异常对象、msg
    :param args_msg: 额外添加的参数, 用于拼接msg
    """
    err_obj, msg = obj_msg
    try:
        yield
    except err_obj as err:
        if args_msg:  # 如果存在额外的参数, 则拼接.否则返回空元组
            args_msg = (
                # 如果是可迭代对象,并且不是字符串。就用join来拼接
                ", ".join(item)
                if isinstance(item, Iterable) and not isinstance(item, str)
                else item
                for item in args_msg
            )
            msg = msg.format(*args_msg)
        raise MyError(msg) from err


def assert_(if_, msg, *msg_args):
    """
    :param if_: 判断条件（正确的）
    :param msg: msg
    :param msg_args: msg的参数
    """
    # 如果是字符串并且不为空, 则视为表达式, 使用eval运行表达式
    if if_ and isinstance(if_, str):
        if_ = eval(if_)

    # 如果不符合, 则抛出异常
    if not if_:
        if msg_args:  # 如果存在额外的参数, 则拼接.否则返回空元组
            msg_args = (
                # 如果是可迭代对象,并且不是字符串。就用join来拼接
                ", ".join(item)
                if isinstance(item, Iterable) and not isinstance(item, str)
                else item
                for item in msg_args
            )
            msg = msg.format(*msg_args)
        raise SystemExit(msg)  # 不需要回溯也能了解的
        # raise MyError(msg)  # 需要回溯的


class Err:
    json_decode = JSONDecodeError, "json反序列化异常"
    index_err = IndexError, '索引异常：\n"{}" not in "{}"'
    assert_err = AssertionError, '【断言错误】\n{}'
    assert_err_args = AssertionError, '{}'
    args_err_msg = "参数错误：用例函数'{}'的参数必须带默认值"
    ele_input = IndexError, '参数错误：ele不能通过关键字参数传入，必须用位置参数传入'


def decorate_lots(decorator):
    def run(cls):
        for k, func in cls.__dict__.items():
            if not re.match(r'__.+?__', k) and callable(func):
                setattr(cls, k, decorator(func))
        return cls

    return run


def mark(params=''):
    print(f'【标记】{str(params)}  {type(params)}')


@contextlib.contextmanager
def time_pass():
    s_time = time.time()
    yield
    duration = time.time() - s_time
    duration = round(duration, 2)  # 小数点保留两位
    print('运行时间：', duration)


class MyThread(my_threading.Thread):
    def __init__(self, target, group=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
        super().__init__(group=group, name=name, args=args, kwargs=kwargs, daemon=daemon)
        self.target = target
        self.args = args

    def run(self):
        # 不能放在构造函数里, 会变慢
        self.__res = self.target(*self.args)

    def get_res(self):
        # 获取线程返回值
        try:
            return self.__res  # 如果子线程不使用join方法，此处可能会报没有self.result的错误
        except AttributeError:
            raise AttributeError('线程返回值获取错误！不能没有join')


def thread_join(params=None, threads=None, func_main=None):
    # params = [(api01, 1), (api02, 2)]
    t01 = None
    if params:
        t01 = [MyThread(target=i[0], args=(i[1:]), daemon=True)
               for i in params]
    elif threads:
        t01 = threads
    [i.start() for i in t01]
    [i.join() for i in t01]
    res_list = [i.get_res() for i in t01]
    if func_main:
        return func_main(*res_list)
    else:
        return res_list


class OpYaml:
    yaml.warnings({'YAMLLoadWarning': False})
    dir01 = os.path.dirname(__file__)
    f_name = r'\_history.yaml'
    f_path = dir01 + f_name
    key = 'adb_connect'

    def __init__(self, path=None):
        self.path = path or self.f_path
        self.__data = self.__read()
        self.data_list = self.__data[self.key]

    def __read(self):
        with open(self.path, 'r', encoding='utf-8') as fi:
            data = yaml.load(fi)
        return data

    def write(self):
        data = self.__data
        with open(self.path, 'w', encoding='utf-8') as fi:
            yaml.dump(data, fi, allow_unicode=True)


class MyGUI:
    def __init__(self):
        self.window = Tk()

        # 设置主窗口属性(窗口名, 大小, 位置, 背景色等)
        self.window.title('JSON处理工具')
        # window.geometry('800x600+50+50')
        self.window['bg'] = 'GhostWhite'
        self.window.attributes('-alpha', 1)

        # 添加两个文本框
        self.input_text = Text(self.window)
        self.input_text['bg'] = 'pink'
        self.input_text.grid(row=0, column=0, sticky=W)

        self.result_labe = Text(self.window)
        self.result_labe['bg'] = 'blue'
        self.result_labe.grid(row=0, column=2)

        self.to_json_button = Button(
            self.window, text='to_json', bg='lightblue', width=10, command=self.to_json)
        self.to_json_button.grid(row=1, column=1)

        self.window.mainloop()

    # def set_window(self, window):
    def fill_required(self, schema: dict) -> dict:
        pro = 'properties'
        req = 'required'
        items = 'items'
        d01 = schema[pro]
        if d01:
            key_s = [k for k in d01.keys()]
            schema[req] = key_s
            # 第二层
            for k01, v in d01.items():
                if isinstance(v, dict) and d01[k01].get(pro):
                    d02 = schema[pro][k01][pro]
                    if d02:
                        key_s = [k for k in d02.keys()]
                        schema[pro][k01][req] = key_s
                        # 第三层
                        for k02, v in d02.items():
                            if isinstance(v, dict):
                                if d02[k02].get(pro) or d02[k02].get(items):
                                    try:
                                        d03 = schema[pro][k01][pro][k02][pro]
                                    except:
                                        d03 = schema[pro][k01][pro][k02][items][pro]
                                    # print(f'd03: {d03}')
                                    if d03:
                                        key_s = [k for k in d03.keys()]
                                        schema[pro][k01][pro][k02][items][req] = key_s
                                        # 第4层
                                        # for k03, v in d03.items():
                                        #     if isinstance(v, dict) and d01[k01][pro][k02][pro][k03].get(pro):
                                        #         d04 = schema[pro][k01][pro][k02][pro][k03][pro]
                                        #         print(d04)
                                        #         if d04:
                                        #             key_s = [k for k in d04.keys()]
                                        #             schema[pro][k01][pro][k02][pro][k03][req] = key_s
        return schema

    def to_json(self):
        # 置空text
        self.result_labe.delete(0.0, END)

        def to_jsonschema(json_data, result):
            '''
                递归生成jsonschema
            :return:
            '''
            if isinstance(json_data, dict):
                is_null = True
                result.append('{')
                result.append("'type': 'object',")
                result.append("'properties': {")
                for k, v in json_data.items():
                    is_null = False
                    result.append("'%s':" % k)
                    to_jsonschema(v, result)
                    result.append(',')
                if not is_null:
                    result.pop()
                result.append('}')
                result.append('}')
            elif isinstance(json_data, list):
                result.append('{')
                result.append("'type': 'array',")
                result.append("'items': ")
                to_jsonschema(json_data[0], result)
                result.append('}')
            elif isinstance(json_data, int):
                result.append("{")
                result.append("'type': 'number'")
                result.append('}')
            elif isinstance(json_data, str):
                result.append("{")
                result.append("'type': 'string'")
                result.append('}')

            return "".join(result)

        json_data = self.input_text.get(0.0, END).strip().replace("\n", "")
        result = []
        try:
            testdata = to_jsonschema(eval(json_data), result)
            params = eval(testdata)  # 获取初步生成的schema
        except Exception as e:
            self.result_labe.insert(1.0, '输入的JSON数据有问题, 请检查')
        else:
            try:
                params = self.fill_required(params)  # 给required加入所有字段
            except Exception:
                self.result_labe.insert(1.0, '填入required列表时，发生错误')
            else:
                self.result_labe.insert(1.0, json.dumps(params, indent=4))


def p_print(a, li=None):
    if li and callable(a):  # 循环打印
        print(*(a(i) for i in li))
    elif isinstance(a, Iterator):
        print(*a)  # 打印迭代器/生成器
    else:
        print(a)  # 直接打印


if __name__ == '__main__':
    op_yaml = OpYaml()

    # my_gui = MyGUI()
